package com.hoshiibuy.common.aop;

import com.alibaba.fastjson.JSON;
import com.hoshiibuy.common.api.dto.SysLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Date;


/**
 * <p>
 *
 * @author yl
 * @Title: LogableAspect.java
 * </p>
 * <p>
 * @Package: com.hoshiibuy.common.aop
 * <p>
 * @Description: 拦截使用Loggable注解的地方
 * </p>
 * <p>
 * </p>
 */
@Component
@Aspect
@Slf4j
public class LogableAspect {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * @param joinPoint
     * @return
     * @Title: success
     * @Description: 成功返回执行操作
     */
    @AfterReturning(value = "@annotation(Logable)")
    public void success(JoinPoint joinPoint) {
        process(joinPoint);
    }


    /**
     * @param joinPoint
     * @return
     * @Title: error
     * @Description: 异常返回执行操作
     */
    @AfterThrowing(value = "@annotation(Logable)", throwing = "error")
    public void error(JoinPoint joinPoint, Throwable error) {
        process(joinPoint);
    }

    /**
     * <p>
     *
     * @param joinPoint
     * @Title: process
     * </p>
     * <p>
     * @Description: 操作流程
     * </p>
     */
    private void process(JoinPoint joinPoint) {
        try {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            Logable operationLog = method.getAnnotation(Logable.class); // 获取注解对象
            String authName = operationLog.authName();// 获取权限名称
            Object[] args = joinPoint.getArgs();
            StandardEvaluationContext context = setStandardEvaluationContext(method, args);// SPEL上下文
            ExpressionParser parser = new SpelExpressionParser();// 使用SPEL进行key的解析
            Object reqParam = getStandardEvaluationContextValue(parser, context, operationLog.reqParam());// 获取请求参数
            LogableParam logableParam = getStandardEvaluationContextValue(parser, context, operationLog.logableParam());// 日志插入参数
            /*if (null == logableParam) {
                return;
            }
            boolean isClose = logableParam.isClose();
            if (isClose) {
                return;
            }*/
            SysLog sysLog = new SysLog();
            sysLog.setAuthName(authName);
            sysLog.setCreateTime(new Date());
            if (reqParam != null) {
                sysLog.setOpeParam(reqParam.toString());
            }
//            sysLog.setUsrId(logableParam.getUserId());
//            sysLog.setOpeIp(logableParam.getIp());
//            sysLog.setOpeIpAddress(logableParam.getAddress());
            //sysLog.setOpeResult(o.toString());
            sysLog.setType("1");// 操作日志
//            if (o instanceof Throwable) {
//                sysLog.setStatus("2");
//            } else {
//                sysLog.setStatus("1");
//            }
//            String type = logableParam.getType();
//            if ("1".equals(type)) {
                log.warn("系统后台操作成功日志：" + sysLog.toString());
                redisTemplate.opsForList().rightPush("sync~sys~Log~list", JSON.toJSONString(sysLog));
//            } else {
//                log.warn("客户端操作成功日志：" + sysLog.toString());
//            }

        } catch (Exception e) {
            log.error("添加操作成功日志失败", e);
        }
    }

    /**
     * <p>
     *
     * @param method 切入的方法
     * @param args   方法传入的参数
     * @return StandardEvaluationContext对象
     * @Title: setStandardEvaluationContext
     * </p>
     * <p>
     * @Description: 设置 SPEL上下文的值
     * </p>
     */
    private StandardEvaluationContext setStandardEvaluationContext(Method method, Object[] args) {
        if (null == method || null == args) {
            return null;
        }
        // 获取被拦截方法参数名列表(使用Spring支持类库)
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paraNameArr = u.getParameterNames(method);
        // SPEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        if (null != paraNameArr && paraNameArr.length > 0 && args.length > 0 && args.length >= paraNameArr.length) {
            // 把方法参数放入SPEL上下文中
            for (int i = 0; i < paraNameArr.length; i++) {
                context.setVariable(paraNameArr[i], args[i]);
            }
        }
        return context;
    }

    /**
     * <p>
     *
     * @param parser  使用SPEL进行key的解析对象
     * @param context SPEL上下文
     * @param name    需要获取的名称
     * @return 名称为name的值
     * @Title: getStandardEvaluationContextValue
     * </p>
     * <p>
     * @Description: 从SPEL上下文里面获取名称为name的值
     * </p>
     */
    @SuppressWarnings("unchecked")
    private <T> T getStandardEvaluationContextValue(ExpressionParser parser, StandardEvaluationContext context,
                                                    String name) {
        if (null == parser || null == context || StringUtils.isBlank(name)) {
            return null;
        }
        Object object = parser.parseExpression(name).getValue(context);
        if (null != object) {
            T value = (T) object;
            return value;
        }
        return null;
    }

}
